package view.cms.desktop
{
    import flash.events.*;
    import flash.filters.*;
    import flash.geom.*;
    
    import mx.binding.utils.*;
    import mx.core.*;
    import mx.managers.*;
    
    import view.cms.desktop.events.*;
    
	 
    public class DrawingObject extends UIComponent implements IElement, IFocusManagerComponent {
	
		public static var MIN_VISIBLE_AREA:int = 40;
	
        public var loaded:Boolean;
        public var angleWhenSelected:Number;
        public var selectable:Boolean;
        public var minx:Number;
        public var miny:Number;
        private var _rpOnResizeStart:Point;
        private var _relCenter:Point;
        public var points:Array;
        public var centerWhenSelected:Point;
        private var moveStartPoint:Point;
        public var _manipAngleOnCenterChange:Number;
        private var _percentYOnResizeStart:Number;
        private var _cmaxy:Number;
        private var _cmaxx:Number;
        private var moveStartOrigin:Point;
        private var _vpOnResizeStart:Point;
        private var _percentHOnResizeStart:Number;
        private var _absCenter:Point;
        private var _percentWOnResizeStart:Number;
        private var _cminx:Number;
        private var _cminy:Number;
        private var _constrained:Boolean;
        private var moveStartCenter:Point;
        private var _hasShadow:Boolean;
        private var _rotatable:Boolean;
        public var maxy:Number;
        public var maxx:Number;
        private var _percentXOnResizeStart:Number;
        
        public var resizable:Boolean;
        
        private var _locked:Boolean;
        
        private var _angle:Number;
        protected var _maintainAspect:Boolean;
        private var _selected:Boolean;

        public function DrawingObject()
        {
        	resizable = true;
            _selected = false;
            _locked = false;
            _maintainAspect = false;
            selectable = true;
            _rotatable = true;
            _angle = 0;
            loaded = false;
            _manipAngleOnCenterChange = 0;
            _constrained = false;
            this.relCenter = new Point(this.width / 2, this.height / 2);
            points = new Array();
            this.doubleClickEnabled = true;
        }

		[Bindable]
		public function set locked(v:Boolean):void
		{
			this._locked = v;
			
 			if ( v )
				this.deactivate();
			else
				this.activate();
		}
		
		public function get locked():Boolean
		{
			return this._locked;
		}
		
        protected function objectLoaded():void
        {
            this.loaded = true;
            dispatchEvent(new Event("objectLoaded",true));
        }

        public function cloneShape():DrawingObject
        {
            return (null);
        }

        public function get maintainAspect():Boolean
        {
            return (_maintainAspect);
        }
        
        public function set maintainAspect(b:Boolean):void
        {
           this.maintainAspect = b;
        }        

        public function get constrained():Boolean
        {
            return (_constrained);
        }
        
		public function withinYConstraints(x:Number, mode:Number=0):Boolean
		{
            if (_constrained)
            {
                switch (mode)
                {
                    case 0:
                        return ( x < _cmaxy && x > _cminy );
                    case -1:
                        return (x > _cminy);
                    case 1:
                        return (x < _cmaxy);
                };
            };
            
            return false;
        }
        
		public function get numPoints():Number
		{
            return (points.length);
        }
        
        public function setMoveVars(start:Point, angle:Number):void
        {
            this.moveStartOrigin = new Point(this.x, this.y);
            this.moveStartPoint = start;
            this._manipAngleOnCenterChange = angle;
        }
        
        public function shift(evt:ShiftEvent):void
        {
            if ( moveStartOrigin && moveStartPoint )
            {
                this.x = this.moveStartOrigin.x + (evt.nx - this.moveStartPoint.x);
                this.y = this.moveStartOrigin.y + (evt.ny - this.moveStartPoint.y);
                this.centerWhenSelected = new Point(this._absCenter.x, this._absCenter.y);
                invalidateDisplayList();
            };
        }
        
        public function setSelectionVars(angle:Number):void
        {
            this.centerWhenSelected = new Point(this._absCenter.x, this._absCenter.y);
            this.angleWhenSelected = (this.angle - angle);
            this._manipAngleOnCenterChange = angle;
        }
 
        public function resize(evt:ResizeEvent):void
        {
            if (_vpOnResizeStart)
            {
                var ma:Matrix = new Matrix();
                ma.rotate(_manipAngleOnCenterChange);
                var pt:Point = new Point(Math.round(_percentXOnResizeStart * evt.size.x), Math.round(_percentYOnResizeStart * evt.size.y));
                pt = ma.deltaTransformPoint(pt);
                this.x = (_vpOnResizeStart.x + pt.x);
                this.y = (_vpOnResizeStart.y + pt.y);
                var ma2:Matrix = new Matrix();
                ma2.rotate(-angleWhenSelected);
                pt = ma2.deltaTransformPoint(new Point((evt.size.x * _percentWOnResizeStart), (evt.size.y * _percentHOnResizeStart)));
                this.resizePoints(pt.x, pt.y);
                this.centerWhenSelected = new Point(this._absCenter.x, this._absCenter.y);

				dispatchEvent(new Event("updateDrawingShape"));
            };
        }
        
        public function set relCenter(center:Point):void
        {
            this._relCenter = center;
            
            var tlc:Point = this.TLC;

            if ( tlc )
            {
                this._absCenter = new Point(tlc.x + _relCenter.x, tlc.y + _relCenter.y);
            };
        }
        
        public function deactivate():void
        {        	
            removeEventListener(MouseEvent.MOUSE_DOWN, this.selectThis);
            removeEventListener(MouseEvent.DOUBLE_CLICK, this.switchSelectThis);
        }
        
        public function get TLC():Point // transformed local coordinate
        {
            if (relCenter != null)
            {
                var len:Number = _relCenter.length;
                var asin:Number = Math.asin(Math.abs(_relCenter.y) / len);
                var a:Number = Math.PI - (this.angle + Math.PI / 2);
                var b:Number = a - asin;
                b = Math.PI * 2 - (b - Math.PI / 2);
                var pt:Point = Point.polar(len, b);
                var ma:Matrix = new Matrix();
                ma.rotate(-this.angle);
                var pt2:Point = ma.deltaTransformPoint(new Point(-pt.x, -pt.y));
                pt2.x = pt2.x + pt.x + this.x;
                pt2.y = pt2.y + pt.y + this.y;
                return pt2;
            };
            
            return null;
        }
        
        public function setConstraints(maxx:Number, maxy:Number, minx:Number, miny:Number):void
        {
            this._constrained = true;
            _cmaxx = maxx;
            _cmaxy = maxy;
            _cminx = minx;
            _cminy = miny;
        }
        
        public function withinConstraints(x:Number, y:Number):Boolean
        {
            return withinXConstraints(x) && withinYConstraints(y);
        }
        
        override public function set y(y:Number):void
        {
            var tlc:Point = this.TLC;
            super.y = y;
            
            if (tlc)
            {
                this._absCenter = new Point(tlc.x + _relCenter.x, tlc.y + _relCenter.y);
            };
        }
        
        public function get angle():Number{
            return (_angle);
        }

		public function set angle(angle:Number):void
		{
			_angle = angle;
			invalidateDisplayList();
		}

        override public function set x(x:Number):void
        {
            var tlc:Point = this.TLC;
            super.x = x;

            if (tlc)
            {
                this._absCenter = new Point(tlc.x + _relCenter.x, tlc.y + _relCenter.y);
            };
        }
        
        public function activate():void
        {
            this.measure();
            this.relCenter = new Point(this.width / 2, this.height / 2);
            addEventListener(MouseEvent.MOUSE_DOWN, this.selectThis);
            addEventListener(MouseEvent.DOUBLE_CLICK, this.switchSelectThis);
        }
        
        public function addStandardPoints(width:Number, height:Number):void
        {
            this.addPoint(0, 0);
            this.addPoint(width, 0);
            this.addPoint(width, height);
            this.addPoint(0, height);
        }
        
        protected function relativeToAbsolute(pt:Point):Point
        {
            var ma:Matrix = new Matrix();
            ma.rotate(this.angle);
            ma.translate(this._absCenter.x, this._absCenter.y);
            var pt:Point = ma.deltaTransformPoint(new Point(pt.x - relCenter.x, pt.y - relCenter.y));
            pt.x = (pt.x + this._absCenter.x);
            pt.y = (pt.y + this._absCenter.y);
            return pt;
        }
        
        public function nearestXConstraint(x:Number):Number
        {
            if (x < _cminx)
            {
                return _cminx;
            };
            
            if (x > _cmaxx)
            {
                return _cmaxx;
            };
            
            return x;
        }
        
        public function get rotatable():Boolean
        {
            return (_rotatable);
        }
       
        public function set rotatable(b:Boolean):void
        {
            _rotatable = b;
            //dispatchEvent(new Event("selectionChanged", true));
        }
        
        override protected function measure():void
        {
            var w:Number = 0;
            var h:Number = 0;
            var i:int = 0;
            
            while (i < points.length) 
            {
                w = w >= points[i].x ? w : points[i].x;
                h = h >= points[i].y ? h : points[i].y;
                i++;
            };
            this.width = w;
            this.height = h;
            this.relCenter = new Point(this.width / 2, this.height / 2);
        }
        
        public function set selected(b:Boolean):void
        {
            this.setFocus();
            _selected = b;

			
/* 			if ( !b )
			{
				//this.controlPanel.hideControl();
   			} */
       }
       
        public function addPoint(x:Number, y:Number):void
        {
            points.push(new Point(x, y));
            this.measure();
        }
        
//        override public function setFocus():void
//       {
/*             if ((this.parent is Page)){
                Page(this.parent).setFocus();
            } */
            
            //if ( this is RichText )
            //	RichText(this).richTextField.setFocus();
//        }

        public function selectThis(evt:MouseEvent=null):void
        {
            this.setFocus();
            if ( this.selectable && !this.selected )
            {
                if (evt)
                {
                    evt.stopPropagation();
                    dispatchEvent(new SelectEvent(SelectEvent.SELECT, true, true, this, evt.shiftKey));
                } 
                else 
                {
                    dispatchEvent(new SelectEvent(SelectEvent.SELECT, true, true, this, false, false));
                };
            };
        }
        
        public function switchSelectThis(evt:MouseEvent=null):void
        {
            this.setFocus();
            if ( this.selectable && !this.selected )
            {
                if (evt)
                {
                    evt.stopPropagation();
                    dispatchEvent(new SelectEvent(SelectEvent.SWITCH_SELECT, true, true, this, evt.shiftKey));
                } 
                else 
                {
                    dispatchEvent(new SelectEvent(SelectEvent.SWITCH_SELECT, true, true, this, false, false));
                };
            };
        }

        public function getPointAt(i:int, ori:Boolean=false):Point
        {
            if ( i < points.length() )
            {
	            if (ori)
	            {
	            	return new Point(points[i].x, points[i].y);
	            };
	            
	            return relativeToAbsolute(points[i]);
            }
            
            return null;
        }
        
        public function get relCenter():Point
        {
            return (_relCenter);
        }
        
        public function findExtents():void
        {
            if (points.length > 0)
            {
                maxx = 0;
                maxy = 0;
                minx = this.x;
                miny = this.y;
                var ma:Matrix = new Matrix();
                ma.rotate(this.angle);
                var i:int = 0;
                
                while (i < points.length) 
                {
                    var pt:Point = ma.deltaTransformPoint(points[i]);
                    
                    if (pt.x + this.x > maxx)
                    {
                        maxx = pt.x + this.x;
                    };
                    
                    if (pt.y + this.y > maxy)
                    {
                        maxy = pt.y + this.y;
                    };
                    
                    if (pt.x + this.x < minx)
                    {
                        minx = pt.x + this.x;
                    };
                    
                    if (pt.y + this.y < miny)
                    {
                        miny = pt.y + this.y;
                    };
                    
                    i++;
                };
            };
        }
        
        protected function resizePoints(w:Number, h:Number):void
        {
            var i:int = 0;
            
            while (i < points.length) 
            {
                points[i].x = (points[i].x / this.width) * ((w>0) ? w : 1);
                points[i].y = (points[i].y / this.height) * ((h>0) ? h : 1);
                i++;
            };
            
            this.measure();
            this.invalidateDisplayList();
        }
        
        public function withinXConstraints(x:Number, mode:Number=0):Boolean
        {
            if (_constrained)
            {
                switch (mode)
                {
                    case 0:
                        return (x < _cmaxx) && (x > _cminx);
                    case -1:
                        return (x > _cminx);
                    case 1:
                        return (x < _cmaxx);
                };
            };
            
            return false;
        }
        
        public function get selected():Boolean
        {
            return (_selected);
        }
        
        public function set absCenter(pt:Point):void
        {
            this._absCenter = pt;
        }
        
        public function rotateAbout(evt:RotateEvent):void
        {
            var ma:Matrix = new Matrix();
            ma.rotate(this.angleWhenSelected + evt.rotateByAngle);
            var pt:Point = ma.deltaTransformPoint(this.relCenter);
            ma.translate(centerWhenSelected.x - pt.x, centerWhenSelected.y - pt.y);
            var ma2:Matrix = new Matrix();
            ma2.rotate(evt.rotateByAngle - _manipAngleOnCenterChange);
            var pt2:Point = new Point(this.centerWhenSelected.x - evt.rotationCenter.x, this.centerWhenSelected.y - evt.rotationCenter.y);
            pt = ma2.deltaTransformPoint(pt2);
            ma.translate(pt.x - pt2.x, pt.y - pt2.y);
            this._absCenter = new Point(evt.rotationCenter.x + pt.x, evt.rotationCenter.y + pt.y);
            this.angle = this.angleWhenSelected + evt.rotateByAngle;
            this.transform.matrix = ma;
            super.x = this.x;
            super.y = this.y;

        }
        
        public function setResizeVars(vp:Point, rp:Point, angle:Number):void
        {
            this._vpOnResizeStart = vp;
            this._rpOnResizeStart = rp;
            this._manipAngleOnCenterChange = angle;
            
            var ma:Matrix = new Matrix();
            ma.rotate(angleWhenSelected);
            var pt:Point = ma.deltaTransformPoint(new Point(this.width, this.height));
            
            if ((_manipAngleOnCenterChange % (2 * Math.PI)) == 0)
            {
                _percentXOnResizeStart = ((this.x - vp.x) / (rp.x - vp.x));
                _percentYOnResizeStart = ((this.y - vp.y) / (rp.y - vp.y));
                _percentWOnResizeStart = (pt.x / (rp.x - vp.x));
                _percentHOnResizeStart = (pt.y / (rp.y - vp.y));
            } 
            else 
            {
                var ma2:Matrix = new Matrix();
                ma2.rotate(-_manipAngleOnCenterChange);
                var pt2:Point = ma2.deltaTransformPoint(new Point((this.x - vp.x), (this.y - vp.y)));
                var pt3:Point = ma2.deltaTransformPoint(new Point((rp.x - vp.x), (rp.y - vp.y)));
                _percentXOnResizeStart = (pt2.x / pt3.x);
                _percentYOnResizeStart = (pt2.y / pt3.y);
                _percentWOnResizeStart = (pt.x / pt3.x);
                _percentHOnResizeStart = (pt.y / pt3.y);
            };
        }
        public function manualPosition(x:Number, y:Number, angle:Number=NaN, w:Number=NaN, h:Number=NaN):void
        {
            if (!isNaN(angle))
            {
                var ma:Matrix = new Matrix();
                ma.rotate(angle);
                this.transform.matrix = ma;
                this._angle = angle;
            };
            
            if ( !isNaN(w) && !isNaN(h) )
            {
                this.resizePoints(w, h);
            };
            
            this.x = x;
            this.y = y;
        }
        
        public function get absCenter():Point{
            return (_absCenter);
        }
        
        public function nearestYConstraint(y:Number):Number
        {
            if (y < _cminy)
            {
                return _cminy;
            };
            
            if (y > _cmaxy)
            {
                return _cmaxy;
            };
            
            return y;
        }
        
    }
}




